home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / non-ANSI / c-client / bezerk.c next >
C/C++ Source or Header  |  1996-10-15  |  71KB  |  2,341 lines

  1. /*
  2.  * Program:    Berkeley mail routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    20 December 1989
  13.  * Last Edited:    15 October 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. /* Dedication:
  38.  *  This file is dedicated with affection to those Merry Marvels of Musical
  39.  * Madness . . .
  40.  *  ->  The Incomparable Leland Stanford Junior University Marching Band  <-
  41.  * who entertain, awaken, and outrage Stanford fans in the fact of repeated
  42.  * losing seasons and shattered Rose Bowl dreams [Cardinal just don't have
  43.  * HUSKY FEVER!!!].
  44.  *
  45.  */
  46.  
  47. #include <stdio.h>
  48. #include <ctype.h>
  49. #include <errno.h>
  50. extern int errno;        /* just in case */
  51. #include <signal.h>
  52. #include <sys/time.h>        /* must be before osdep.h */
  53. #include "mail.h"
  54. #include "osdep.h"
  55. #include <pwd.h>
  56. #include <sys/stat.h>
  57. #include "bezerk.h"
  58. #include "rfc822.h"
  59. #include "misc.h"
  60. #include "dummy.h"
  61.  
  62. /* Berkeley mail routines */
  63.  
  64.  
  65. /* Driver dispatch used by MAIL */
  66.  
  67. DRIVER bezerkdriver = {
  68.   "bezerk",            /* driver name */
  69.   (DRIVER *) NIL,        /* next driver */
  70.   bezerk_valid,            /* mailbox is valid for us */
  71.   bezerk_parameters,        /* manipulate parameters */
  72.   bezerk_find,            /* find mailboxes */
  73.   bezerk_find_bboards,        /* find bboards */
  74.   bezerk_find_all,        /* find all mailboxes */
  75.   bezerk_find_all_bboards,    /* find all bboards */
  76.   bezerk_subscribe,        /* subscribe to mailbox */
  77.   bezerk_unsubscribe,        /* unsubscribe from mailbox */
  78.   bezerk_subscribe_bboard,    /* subscribe to bboard */
  79.   bezerk_unsubscribe_bboard,    /* unsubscribe from bboard */
  80.   bezerk_create,        /* create mailbox */
  81.   bezerk_delete,        /* delete mailbox */
  82.   bezerk_rename,        /* rename mailbox */
  83.   bezerk_open,            /* open mailbox */
  84.   bezerk_close,            /* close mailbox */
  85.   bezerk_fetchfast,        /* fetch message "fast" attributes */
  86.   bezerk_fetchflags,        /* fetch message flags */
  87.   bezerk_fetchstructure,    /* fetch message envelopes */
  88.   bezerk_fetchheader,        /* fetch message header only */
  89.   bezerk_fetchtext,        /* fetch message body only */
  90.   bezerk_fetchbody,        /* fetch message body section */
  91.   bezerk_setflag,        /* set message flag */
  92.   bezerk_clearflag,        /* clear message flag */
  93.   bezerk_search,        /* search for message based on criteria */
  94.   bezerk_ping,            /* ping mailbox to see if still alive */
  95.   bezerk_check,            /* check for new messages */
  96.   bezerk_expunge,        /* expunge deleted messages */
  97.   bezerk_copy,            /* copy messages to another mailbox */
  98.   bezerk_move,            /* move messages to another mailbox */
  99.   bezerk_append,        /* append string message to mailbox */
  100.   bezerk_gc            /* garbage collect stream */
  101. };
  102.  
  103.                 /* prototype stream */
  104. MAILSTREAM bezerkproto = {&bezerkdriver};
  105.  
  106.                 /* driver parameters */
  107. static long bezerk_fromwidget = T;
  108.  
  109. /* Berkeley mail validate mailbox
  110.  * Accepts: mailbox name
  111.  * Returns: our driver if name is valid, NIL otherwise
  112.  */
  113.  
  114. DRIVER *bezerk_valid (name)
  115.     char *name;
  116. {
  117.   char tmp[MAILTMPLEN];
  118.   return bezerk_isvalid (name,tmp) ? &bezerkdriver : NIL;
  119. }
  120.  
  121.  
  122. /* Berkeley mail test for valid mailbox name
  123.  * Accepts: mailbox name
  124.  *        scratch buffer
  125.  * Returns: T if valid, NIL otherwise
  126.  */
  127.  
  128. int bezerk_isvalid (name,tmp)
  129.     char *name;
  130.     char *tmp;
  131. {
  132.   int fd;
  133.   int ret = NIL;
  134.   char *t,file[MAILTMPLEN];
  135.   struct stat sbuf;
  136.   time_t tp[2];
  137.   errno = EINVAL;        /* assume invalid argument */
  138.                 /* must be non-empty file */
  139.   if ((*name != '{') && !((*name == '*') && (name[1] == '{')) &&
  140.       (t = dummy_file (file,name)) && !stat (t,&sbuf)) {
  141.     if (!sbuf.st_size)errno = 0;/* empty file */
  142.     else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
  143.                 /* error -1 for invalid format */
  144.       if (!(ret = bezerk_isvalid_fd (fd,tmp))) errno = -1;
  145.       close (fd);        /* close the file */
  146.       tp[0] = sbuf.st_atime;    /* preserve atime and mtime */
  147.       tp[1] = sbuf.st_mtime;
  148.       utime (file,tp);        /* set the times */
  149.     }
  150.   }
  151.   return ret;            /* return what we should */
  152. }
  153.  
  154. /* Berkeley mail test for valid mailbox
  155.  * Accepts: file descriptor
  156.  *        scratch buffer
  157.  * Returns: T if valid, NIL otherwise
  158.  */
  159.  
  160. long bezerk_isvalid_fd (fd,tmp)
  161.     int fd;
  162.     char *tmp;
  163. {
  164.   int zn;
  165.   int ret = NIL;
  166.   char *s,*t,c = '\n';
  167.   memset (tmp,'\0',MAILTMPLEN);
  168.   if (read (fd,tmp,MAILTMPLEN-1) >= 0) {
  169.     for (s = tmp; (*s == '\n') || (*s == ' ') || (*s == '\t');) c = *s++;
  170.     if (c == '\n') VALID (s,t,ret,zn);
  171.   }
  172.   return ret;            /* return what we should */
  173. }
  174.  
  175.  
  176. /* Berkeley manipulate driver parameters
  177.  * Accepts: function code
  178.  *        function-dependent value
  179.  * Returns: function-dependent return value
  180.  */
  181.  
  182. void *bezerk_parameters (function,value)
  183.     long function;
  184.     void *value;
  185. {
  186.   switch ((int) function) {
  187.   case SET_FROMWIDGET:
  188.     bezerk_fromwidget = (long) value;
  189.     break;
  190.   case GET_FROMWIDGET:
  191.     value = (void *) bezerk_fromwidget;
  192.     break;
  193.   default:
  194.     value = NIL;        /* error case */
  195.     break;
  196.   }
  197.   return value;
  198. }
  199.  
  200. /* Berkeley mail find list of mailboxes
  201.  * Accepts: mail stream
  202.  *        pattern to search
  203.  */
  204.  
  205. void bezerk_find (stream,pat)
  206.     MAILSTREAM *stream;
  207.     char *pat;
  208. {
  209.   if (stream) dummy_find (NIL,pat);
  210. }
  211.  
  212.  
  213. /* Berkeley mail find list of bboards
  214.  * Accepts: mail stream
  215.  *        pattern to search
  216.  */
  217.  
  218. void bezerk_find_bboards (stream,pat)
  219.     MAILSTREAM *stream;
  220.     char *pat;
  221. {
  222.   if (stream) dummy_find_bboards (NIL,pat);
  223. }
  224.  
  225.  
  226. /* Berkeley mail find list of all mailboxes
  227.  * Accepts: mail stream
  228.  *        pattern to search
  229.  */
  230.  
  231. void bezerk_find_all (stream,pat)
  232.     MAILSTREAM *stream;
  233.     char *pat;
  234. {
  235.   if (stream) dummy_find_all (NIL,pat);
  236. }
  237.  
  238.  
  239. /* Berkeley mail find list of all bboards
  240.  * Accepts: mail stream
  241.  *        pattern to search
  242.  */
  243.  
  244. void bezerk_find_all_bboards (stream,pat)
  245.     MAILSTREAM *stream;
  246.     char *pat;
  247. {
  248.   if (stream) dummy_find_all_bboards (NIL,pat);
  249. }
  250.  
  251. /* Berkeley mail subscribe to mailbox
  252.  * Accepts: mail stream
  253.  *        mailbox to add to subscription list
  254.  * Returns: T on success, NIL on failure
  255.  */
  256.  
  257. long bezerk_subscribe (stream,mailbox)
  258.     MAILSTREAM *stream;
  259.     char *mailbox;
  260. {
  261.   char tmp[MAILTMPLEN];
  262.   return sm_subscribe (dummy_file (tmp,mailbox));
  263. }
  264.  
  265.  
  266. /* Berkeley mail unsubscribe to mailbox
  267.  * Accepts: mail stream
  268.  *        mailbox to delete from subscription list
  269.  * Returns: T on success, NIL on failure
  270.  */
  271.  
  272. long bezerk_unsubscribe (stream,mailbox)
  273.     MAILSTREAM *stream;
  274.     char *mailbox;
  275. {
  276.   char tmp[MAILTMPLEN];
  277.   return sm_unsubscribe (dummy_file (tmp,mailbox));
  278. }
  279.  
  280.  
  281. /* Berkeley mail subscribe to bboard
  282.  * Accepts: mail stream
  283.  *        bboard to add to subscription list
  284.  * Returns: T on success, NIL on failure
  285.  */
  286.  
  287. long bezerk_subscribe_bboard (stream,mailbox)
  288.     MAILSTREAM *stream;
  289.     char *mailbox;
  290. {
  291.   return NIL;            /* never valid for Bezerk */
  292. }
  293.  
  294.  
  295. /* Berkeley mail unsubscribe to bboard
  296.  * Accepts: mail stream
  297.  *        bboard to delete from subscription list
  298.  * Returns: T on success, NIL on failure
  299.  */
  300.  
  301. long bezerk_unsubscribe_bboard (stream,mailbox)
  302.     MAILSTREAM *stream;
  303.     char *mailbox;
  304. {
  305.   return NIL;            /* never valid for Bezerk */
  306. }
  307.  
  308. /* Berkeley mail create mailbox
  309.  * Accepts: MAIL stream
  310.  *        mailbox name to create
  311.  * Returns: T on success, NIL on failure
  312.  */
  313.  
  314. long bezerk_create (stream,mailbox)
  315.     MAILSTREAM *stream;
  316.     char *mailbox;
  317. {
  318.   return dummy_create (stream,mailbox);
  319. }
  320.  
  321.  
  322. /* Berkeley mail delete mailbox
  323.  * Accepts: MAIL stream
  324.  *        mailbox name to delete
  325.  * Returns: T on success, NIL on failure
  326.  */
  327.  
  328. long bezerk_delete (stream,mailbox)
  329.     MAILSTREAM *stream;
  330.     char *mailbox;
  331. {
  332.   return bezerk_rename (stream,mailbox,NIL);
  333. }
  334.  
  335. /* Berkeley mail rename mailbox
  336.  * Accepts: MAIL stream
  337.  *        old mailbox name
  338.  *        new mailbox name (or NIL for delete)
  339.  * Returns: T on success, NIL on failure
  340.  */
  341.  
  342. long bezerk_rename (stream,old,new)
  343.     MAILSTREAM *stream;
  344.     char *old;
  345.     char *new;
  346. {
  347.   long ret = T;
  348.   char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN],lockx[MAILTMPLEN];
  349.   int fd,ld;
  350.                 /* get the c-client lock */
  351.   if (!lockname (lock,dummy_file (file,old))) return NIL;
  352.   if ((ld = open (lock,O_RDWR|O_CREAT,
  353.           (int) mail_parameters (NIL,GET_LOCKPROTECTION,NIL))) < 0) {
  354.     sprintf (tmp,"Can't get lock for mailbox %s: %s",old,strerror (errno));
  355.     mm_log (tmp,ERROR);
  356.     return NIL;
  357.   }
  358.                 /* lock out other c-clients */
  359.   if (flock (ld,LOCK_EX|LOCK_NB)) {
  360.     close (ld);            /* couldn't lock, give up on it then */
  361.     sprintf (tmp,"Mailbox %s is in use by another process",old);
  362.     mm_log (tmp,ERROR);
  363.     return NIL;
  364.   }
  365.                 /* lock out non c-client applications */
  366.   if ((fd = bezerk_lock (file,O_RDWR,S_IREAD|S_IWRITE,lockx,LOCK_EX)) < 0) {
  367.     sprintf (tmp,"Can't lock mailbox %s: %s",old,strerror (errno));
  368.     mm_log (tmp,ERROR);
  369.     return NIL;
  370.   }
  371.                 /* do the rename or delete operation */
  372.   if (new ? rename (file,dummy_file (tmp,new)) : unlink (file)) {
  373.     sprintf (tmp,"Can't %s mailbox %s: %s",new ? "rename" : "delete",old,
  374.          strerror (errno));
  375.     mm_log (tmp,ERROR);
  376.     ret = NIL;            /* set failure */
  377.   }
  378.   bezerk_unlock (fd,NIL,lockx);    /* unlock and close mailbox */
  379.   flock (ld,LOCK_UN);        /* release c-client lock lock */
  380.   close (ld);            /* close c-client lock */
  381.   unlink (lock);        /* and delete it */
  382.   return ret;            /* return success */
  383. }
  384.  
  385. /* Berkeley mail open
  386.  * Accepts: Stream to open
  387.  * Returns: Stream on success, NIL on failure
  388.  */
  389.  
  390. MAILSTREAM *bezerk_open (stream)
  391.     MAILSTREAM *stream;
  392. {
  393.   long i;
  394.   int fd;
  395.   char tmp[MAILTMPLEN];
  396.   struct stat sbuf;
  397.   long retry;
  398.                 /* return prototype for OP_PROTOTYPE call */
  399.   if (!stream) return &bezerkproto;
  400.   retry = stream->silent ? 1 : KODRETRY;
  401.   if (LOCAL) {            /* close old file if stream being recycled */
  402.     bezerk_close (stream);    /* dump and save the changes */
  403.     stream->dtb = &bezerkdriver;/* reattach this driver */
  404.     mail_free_cache (stream);    /* clean up cache */
  405.   }
  406.   stream->local = fs_get (sizeof (BEZERKLOCAL));
  407.                 /* canonicalize the stream mailbox name */
  408.   dummy_file (tmp,stream->mailbox);
  409.                 /* force readonly if bboard */
  410.   if (*stream->mailbox == '*') stream->rdonly = T;
  411.   else {            /* canonicalize name */
  412.     fs_give ((void **) &stream->mailbox);
  413.     stream->mailbox = cpystr (tmp);
  414.   }
  415.   /* You may wonder why LOCAL->name is needed.  It isn't at all obvious from
  416.    * the code.  The problem is that when a stream is recycled with another
  417.    * mailbox of the same type, the driver's close method isn't called because
  418.    * it could be IMAP and closing then would defeat the entire point of
  419.    * recycling.  Hence there is code in the file drivers to call the close
  420.    * method such as what appears above.  The problem is, by this point,
  421.    * mail_open() has already changed the stream->mailbox name to point to the
  422.    * new name, and bezerk_close() needs the old name.
  423.    */
  424.   LOCAL->name = cpystr (tmp);    /* local copy for recycle case */
  425.   LOCAL->ld = NIL;        /* no state locking yet */
  426.   LOCAL->lname = NIL;
  427.   LOCAL->filesize = 0;        /* initialize file information */
  428.   LOCAL->filetime = 0;
  429.   LOCAL->msgs = NIL;        /* no cache yet */
  430.   LOCAL->cachesize = 0;
  431.   LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNK) + 1);
  432.   stream->sequence++;        /* bump sequence number */
  433.  
  434.   LOCAL->dirty = NIL;        /* no update yet */
  435.                 /* make lock for read/write access */
  436.   if (!stream->rdonly) while (retry) {
  437.                 /* get a new file handle each time */
  438.     if (!lockname (tmp,LOCAL->name) ||
  439.     ((fd = open (tmp,O_RDWR|O_CREAT,
  440.             (int) mail_parameters (NIL,GET_LOCKPROTECTION,NIL))) < 0)){
  441.       mm_log ("Can't open mailbox lock, access is readonly",WARN);
  442.       retry = 0;        /* give up */
  443.     }
  444.                 /* can get the lock? */
  445.     else if (flock (fd,LOCK_EX|LOCK_NB)) {
  446.       if (retry-- == KODRETRY) {/* no, first time through? */
  447.                 /* yes, get other process' PID */
  448.     if (!fstat (fd,&sbuf) && (i = min (sbuf.st_size,MAILTMPLEN)) &&
  449.         (read (fd,tmp,i) == i) && !(tmp[i] = 0) && (i = atol (tmp))) {
  450.       kill ((int) i,SIGUSR2);
  451.       sprintf (tmp,"Trying to get mailbox lock from process %ld",i);
  452.       mm_log (tmp,WARN);
  453.     }
  454.     else retry = 0;        /* give up */
  455.       }
  456.       close (fd);        /* get a new handle next time around */
  457.       if (!stream->silent) {    /* nothing if silent stream */
  458.     if (retry) sleep (1);    /* wait a second before trying again */
  459.     else mm_log ("Mailbox is open by another process, access is readonly",
  460.              WARN);
  461.       }
  462.     }
  463.     else {            /* got the lock, nobody else can alter state */
  464.       LOCAL->ld = fd;        /* note lock's fd and name */
  465.       LOCAL->lname = cpystr (tmp);
  466.                 /* make sure mode OK (don't use fchmod()) */
  467.       chmod (LOCAL->lname,(int) mail_parameters (NIL,GET_LOCKPROTECTION,NIL));
  468.       if (stream->silent) i = 0;/* silent streams won't accept KOD */
  469.       else {            /* note our PID in the lock */
  470.     sprintf (tmp,"%d",getpid ());
  471.     write (fd,tmp,(i = strlen (tmp))+1);
  472.       }
  473.       ftruncate (fd,i);        /* make sure tied off */
  474.       fsync (fd);        /* make sure it's available */
  475.       retry = 0;        /* no more need to try */
  476.     }
  477.   }
  478.  
  479.                 /* parse mailbox */
  480.   stream->nmsgs = stream->recent = 0;
  481.                 /* will we be able to get write access? */
  482.   if (LOCAL->ld && access (LOCAL->name,W_OK) && (errno == EACCES)) {
  483.     mm_log ("Can't get write access to mailbox, access is readonly",WARN);
  484.     flock (LOCAL->ld,LOCK_UN);    /* release the lock */
  485.     close (LOCAL->ld);        /* close the lock file */
  486.     LOCAL->ld = NIL;        /* no more lock fd */
  487.     unlink (LOCAL->lname);    /* delete it */
  488.     fs_give ((void **) &LOCAL->lname);
  489.   }
  490.                 /* abort if can't get RW silent stream */
  491.   if (stream->silent && !stream->rdonly && !LOCAL->ld) bezerk_abort (stream);
  492.                 /* parse mailbox */
  493.   else if ((fd = bezerk_parse (stream,tmp,LOCK_SH)) >= 0) {
  494.     bezerk_unlock (fd,stream,tmp);
  495.     mail_unlock (stream);
  496.   }
  497.   if (!LOCAL) return NIL;    /* failure if stream died */
  498.   stream->rdonly = !LOCAL->ld;    /* make sure upper level knows readonly */
  499.                 /* notify about empty mailbox */
  500.   if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",NIL);
  501.   return stream;        /* return stream alive to caller */
  502. }
  503.  
  504. /* Berkeley mail close
  505.  * Accepts: MAIL stream
  506.  */
  507.  
  508. void bezerk_close (stream)
  509.     MAILSTREAM *stream;
  510. {
  511.   int silent = stream->silent;
  512.   stream->silent = T;        /* note this stream is dying */
  513.   bezerk_check (stream);    /* dump final checkpoint */
  514.   stream->silent = silent;    /* restore previous status */
  515.   bezerk_abort (stream);    /* now punt the file and local data */
  516. }
  517.  
  518.  
  519. /* Berkeley mail fetch fast information
  520.  * Accepts: MAIL stream
  521.  *        sequence
  522.  */
  523.  
  524. void bezerk_fetchfast (stream,sequence)
  525.     MAILSTREAM *stream;
  526.     char *sequence;
  527. {
  528.   return;            /* no-op for local mail */
  529. }
  530.  
  531.  
  532. /* Berkeley mail fetch flags
  533.  * Accepts: MAIL stream
  534.  *        sequence
  535.  */
  536.  
  537. void bezerk_fetchflags (stream,sequence)
  538.     MAILSTREAM *stream;
  539.     char *sequence;
  540. {
  541.   return;            /* no-op for local mail */
  542. }
  543.  
  544. /* Berkeley mail fetch structure
  545.  * Accepts: MAIL stream
  546.  *        message # to fetch
  547.  *        pointer to return body
  548.  * Returns: envelope of this message, body returned in body value
  549.  *
  550.  * Fetches the "fast" information as well
  551.  */
  552.  
  553. ENVELOPE *bezerk_fetchstructure (stream,msgno,body)
  554.     MAILSTREAM *stream;
  555.     long msgno;
  556.     BODY **body;
  557. {
  558.   ENVELOPE **env;
  559.   BODY **b;
  560.   STRING bs;
  561.   LONGCACHE *lelt;
  562.   FILECACHE *m = LOCAL->msgs[msgno - 1];
  563.   long i = max (m->headersize,m->bodysize);
  564.   if (stream->scache) {        /* short cache */
  565.     if (msgno != stream->msgno){/* flush old poop if a different message */
  566.       mail_free_envelope (&stream->env);
  567.       mail_free_body (&stream->body);
  568.     }
  569.     stream->msgno = msgno;
  570.     env = &stream->env;        /* get pointers to envelope and body */
  571.     b = &stream->body;
  572.   }
  573.   else {            /* long cache */
  574.     lelt = mail_lelt (stream,msgno);
  575.     env = &lelt->env;        /* get pointers to envelope and body */
  576.     b = &lelt->body;
  577.   }
  578.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  579.     mail_free_envelope (env);    /* flush old envelope and body */
  580.     mail_free_body (b);
  581.     if (i > LOCAL->buflen) {    /* make sure enough buffer space */
  582.       fs_give ((void **) &LOCAL->buf);
  583.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
  584.     }
  585.     INIT (&bs,mail_string,(void *) m->body,m->bodysize);
  586.                 /* parse envelope and body */
  587.     rfc822_parse_msg (env,body ? b : NIL,m->header,m->headersize,&bs,
  588.               mylocalhost (),LOCAL->buf);
  589.   }
  590.   if (body) *body = *b;        /* return the body */
  591.   return *env;            /* return the envelope */
  592. }
  593.  
  594. /* Berkeley mail snarf message, only for Tenex driver
  595.  * Accepts: MAIL stream
  596.  *        message # to snarf
  597.  *        pointer to size to return
  598.  * Returns: message text in RFC822 format
  599.  */
  600.  
  601. char *bezerk_snarf (stream,msgno,size)
  602.     MAILSTREAM *stream;
  603.     long msgno;
  604.     long *size;
  605. {
  606.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  607.   FILECACHE *m = LOCAL->msgs[msgno - 1];
  608.   if (((*size = m->headersize + m->bodysize) > LOCAL->buflen) ||
  609.       LOCAL->buflen > CHUNK) {    /* make sure stream can hold the text */
  610.                 /* fs_resize would do an unnecessary copy */
  611.     fs_give ((void **) &LOCAL->buf);
  612.     LOCAL->buf = (char *) fs_get((LOCAL->buflen = max (*size,(long) CHUNK))+1);
  613.   }
  614.                 /* copy the text */
  615.   if (m->headersize) memcpy (LOCAL->buf,m->header,m->headersize);
  616.   if (m->bodysize) memcpy (LOCAL->buf + m->headersize,m->body,m->bodysize);
  617.   LOCAL->buf[*size] = '\0';    /* tie off string */
  618.   return LOCAL->buf;
  619. }
  620.  
  621. /* Berkeley mail fetch message header
  622.  * Accepts: MAIL stream
  623.  *        message # to fetch
  624.  * Returns: message header in RFC822 format
  625.  */
  626.  
  627. char *bezerk_fetchheader (stream,msgno)
  628.     MAILSTREAM *stream;
  629.     long msgno;
  630. {
  631.   FILECACHE *m = LOCAL->msgs[msgno - 1];
  632.                 /* copy the string */
  633.   strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,m->header,m->headersize);
  634.   return LOCAL->buf;
  635. }
  636.  
  637.  
  638. /* Berkeley mail fetch message text (only)
  639.     body only;
  640.  * Accepts: MAIL stream
  641.  *        message # to fetch
  642.  * Returns: message text in RFC822 format
  643.  */
  644.  
  645. char *bezerk_fetchtext (stream,msgno)
  646.     MAILSTREAM *stream;
  647.     long msgno;
  648. {
  649.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  650.   FILECACHE *m = LOCAL->msgs[msgno - 1];
  651.   if (!elt->seen) {        /* if message not seen */
  652.     elt->seen = T;        /* mark message as seen */
  653.                 /* recalculate Status/X-Status lines */
  654.     bezerk_update_status (m->status,elt);
  655.     LOCAL->dirty = T;        /* note stream is now dirty */
  656.   }
  657.   strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,m->body,m->bodysize);
  658.   return LOCAL->buf;
  659. }
  660.  
  661. /* Berkeley fetch message body as a structure
  662.  * Accepts: Mail stream
  663.  *        message # to fetch
  664.  *        section specifier
  665.  *        pointer to length
  666.  * Returns: pointer to section of message body
  667.  */
  668.  
  669. char *bezerk_fetchbody (stream,m,s,len)
  670.     MAILSTREAM *stream;
  671.     long m;
  672.     char *s;
  673.     unsigned long *len;
  674. {
  675.   BODY *b;
  676.   PART *pt;
  677.   unsigned long i;
  678.   char *base = LOCAL->msgs[m - 1]->body;
  679.   unsigned long offset = 0;
  680.   MESSAGECACHE *elt = mail_elt (stream,m);
  681.                 /* make sure have a body */
  682.   if (!(bezerk_fetchstructure (stream,m,&b) && b && s && *s &&
  683.     ((i = strtol (s,&s,10)) > 0))) return NIL;
  684.   do {                /* until find desired body part */
  685.                 /* multipart content? */
  686.     if (b->type == TYPEMULTIPART) {
  687.       pt = b->contents.part;    /* yes, find desired part */
  688.       while (--i && (pt = pt->next));
  689.       if (!pt) return NIL;    /* bad specifier */
  690.                 /* note new body, check valid nesting */
  691.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  692.       offset = pt->offset;    /* get new offset */
  693.     }
  694.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  695.                 /* need to go down further? */
  696.     if (i = *s) switch (b->type) {
  697.     case TYPEMESSAGE:        /* embedded message */
  698.       offset = b->contents.msg.offset;
  699.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  700.     case TYPEMULTIPART:        /* multipart, get next section */
  701.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  702.     default:            /* bogus subpart specification */
  703.       return NIL;
  704.     }
  705.   } while (i);
  706.                 /* lose if body bogus */
  707.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  708.   if (!elt->seen) {        /* if message not seen */
  709.     elt->seen = T;        /* mark message as seen */
  710.                 /* recalculate Status/X-Status lines */
  711.     bezerk_update_status (LOCAL->msgs[m - 1]->status,elt);
  712.     LOCAL->dirty = T;        /* note stream is now dirty */
  713.   }
  714.   return rfc822_contents (&LOCAL->buf,&LOCAL->buflen,len,base + offset,
  715.               b->size.ibytes,b->encoding);
  716. }
  717.  
  718. /* Berkeley mail set flag
  719.  * Accepts: MAIL stream
  720.  *        sequence
  721.  *        flag(s)
  722.  */
  723.  
  724. void bezerk_setflag (stream,sequence,flag)
  725.     MAILSTREAM *stream;
  726.     char *sequence;
  727.     char *flag;
  728. {
  729.   MESSAGECACHE *elt;
  730.   long i;
  731.   short f = bezerk_getflags (stream,flag);
  732.   if (!f) return;        /* no-op if no flags to modify */
  733.                 /* get sequence and loop on it */
  734.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  735.     if ((elt = mail_elt (stream,i))->sequence) {
  736.                 /* set all requested flags */
  737.       if (f&fSEEN) elt->seen = T;
  738.       if (f&fDELETED) elt->deleted = T;
  739.       if (f&fFLAGGED) elt->flagged = T;
  740.       if (f&fANSWERED) elt->answered = T;
  741.                 /* recalculate Status/X-Status lines */
  742.       bezerk_update_status (LOCAL->msgs[i - 1]->status,elt);
  743.       LOCAL->dirty = T;        /* note stream is now dirty */
  744.     }
  745. }
  746.  
  747.  
  748. /* Berkeley mail clear flag
  749.  * Accepts: MAIL stream
  750.  *        sequence
  751.  *        flag(s)
  752.  */
  753.  
  754. void bezerk_clearflag (stream,sequence,flag)
  755.     MAILSTREAM *stream;
  756.     char *sequence;
  757.     char *flag;
  758. {
  759.   MESSAGECACHE *elt;
  760.   long i;
  761.   short f = bezerk_getflags (stream,flag);
  762.   if (!f) return;        /* no-op if no flags to modify */
  763.                 /* get sequence and loop on it */
  764.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  765.     if ((elt = mail_elt (stream,i))->sequence) {
  766.                 /* clear all requested flags */
  767.       if (f&fSEEN) elt->seen = NIL;
  768.       if (f&fDELETED) elt->deleted = NIL;
  769.       if (f&fFLAGGED) elt->flagged = NIL;
  770.       if (f&fANSWERED) elt->answered = NIL;
  771.                 /* recalculate Status/X-Status lines */
  772.       bezerk_update_status (LOCAL->msgs[i - 1]->status,elt);
  773.       LOCAL->dirty = T;        /* note stream is now dirty */
  774.     }
  775. }
  776.  
  777. /* Berkeley mail search for messages
  778.  * Accepts: MAIL stream
  779.  *        search criteria
  780.  */
  781.  
  782. void bezerk_search (stream,criteria)
  783.     MAILSTREAM *stream;
  784.     char *criteria;
  785. {
  786.   long i,n;
  787.   char *d;
  788.   search_t f;
  789.                 /* initially all searched */
  790.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  791.                 /* get first criterion */
  792.   if (criteria && (criteria = strtok (criteria," "))) {
  793.                 /* for each criterion */
  794.     for (; criteria; (criteria = strtok (NIL," "))) {
  795.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  796.       switch (*ucase (criteria)) {
  797.       case 'A':            /* possible ALL, ANSWERED */
  798.     if (!strcmp (criteria+1,"LL")) f = bezerk_search_all;
  799.     else if (!strcmp (criteria+1,"NSWERED")) f = bezerk_search_answered;
  800.     break;
  801.       case 'B':            /* possible BCC, BEFORE, BODY */
  802.     if (!strcmp (criteria+1,"CC"))
  803.       f = bezerk_search_string (bezerk_search_bcc,&d,&n);
  804.     else if (!strcmp (criteria+1,"EFORE"))
  805.       f = bezerk_search_date (bezerk_search_before,&n);
  806.     else if (!strcmp (criteria+1,"ODY"))
  807.       f = bezerk_search_string (bezerk_search_body,&d,&n);
  808.     break;
  809.       case 'C':            /* possible CC */
  810.     if (!strcmp (criteria+1,"C"))
  811.       f = bezerk_search_string (bezerk_search_cc,&d,&n);
  812.     break;
  813.       case 'D':            /* possible DELETED */
  814.     if (!strcmp (criteria+1,"ELETED")) f = bezerk_search_deleted;
  815.     break;
  816.       case 'F':            /* possible FLAGGED, FROM */
  817.     if (!strcmp (criteria+1,"LAGGED")) f = bezerk_search_flagged;
  818.     else if (!strcmp (criteria+1,"ROM"))
  819.       f = bezerk_search_string (bezerk_search_from,&d,&n);
  820.     break;
  821.       case 'K':            /* possible KEYWORD */
  822.     if (!strcmp (criteria+1,"EYWORD"))
  823.       f = bezerk_search_flag (bezerk_search_keyword,&d);
  824.     break;
  825.       case 'N':            /* possible NEW */
  826.     if (!strcmp (criteria+1,"EW")) f = bezerk_search_new;
  827.     break;
  828.  
  829.       case 'O':            /* possible OLD, ON */
  830.     if (!strcmp (criteria+1,"LD")) f = bezerk_search_old;
  831.     else if (!strcmp (criteria+1,"N"))
  832.       f = bezerk_search_date (bezerk_search_on,&n);
  833.     break;
  834.       case 'R':            /* possible RECENT */
  835.     if (!strcmp (criteria+1,"ECENT")) f = bezerk_search_recent;
  836.     break;
  837.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  838.     if (!strcmp (criteria+1,"EEN")) f = bezerk_search_seen;
  839.     else if (!strcmp (criteria+1,"INCE"))
  840.       f = bezerk_search_date (bezerk_search_since,&n);
  841.     else if (!strcmp (criteria+1,"UBJECT"))
  842.       f = bezerk_search_string (bezerk_search_subject,&d,&n);
  843.     break;
  844.       case 'T':            /* possible TEXT, TO */
  845.     if (!strcmp (criteria+1,"EXT"))
  846.       f = bezerk_search_string (bezerk_search_text,&d,&n);
  847.     else if (!strcmp (criteria+1,"O"))
  848.       f = bezerk_search_string (bezerk_search_to,&d,&n);
  849.     break;
  850.       case 'U':            /* possible UN* */
  851.     if (criteria[1] == 'N') {
  852.       if (!strcmp (criteria+2,"ANSWERED")) f = bezerk_search_unanswered;
  853.       else if (!strcmp (criteria+2,"DELETED")) f = bezerk_search_undeleted;
  854.       else if (!strcmp (criteria+2,"FLAGGED")) f = bezerk_search_unflagged;
  855.       else if (!strcmp (criteria+2,"KEYWORD"))
  856.         f = bezerk_search_flag (bezerk_search_unkeyword,&d);
  857.       else if (!strcmp (criteria+2,"SEEN")) f = bezerk_search_unseen;
  858.     }
  859.     break;
  860.       default:            /* we will barf below */
  861.     break;
  862.       }
  863.       if (!f) {            /* if can't determine any criteria */
  864.     sprintf (LOCAL->buf,"Unknown search criterion: %.30s",criteria);
  865.     mm_log (LOCAL->buf,ERROR);
  866.     return;
  867.       }
  868.                 /* run the search criterion */
  869.       for (i = 1; i <= stream->nmsgs; ++i)
  870.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  871.       mail_elt (stream,i)->searched = NIL;
  872.     }
  873.                 /* report search results to main program */
  874.     for (i = 1; i <= stream->nmsgs; ++i)
  875.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  876.   }
  877. }
  878.  
  879. /* Berkeley mail ping mailbox
  880.  * Accepts: MAIL stream
  881.  * Returns: T if stream alive, else NIL
  882.  * No-op for readonly files, since read/writer can expunge it from under us!
  883.  */
  884.  
  885. long bezerk_ping (stream)
  886.     MAILSTREAM *stream;
  887. {
  888.   char lock[MAILTMPLEN];
  889.   struct stat sbuf;
  890.   int fd;
  891.                 /* does he want to give up readwrite? */
  892.   if (stream->rdonly && LOCAL->ld) {
  893.     flock (LOCAL->ld,LOCK_UN);    /* yes, release the lock */
  894.     close (LOCAL->ld);        /* close the lock file */
  895.     LOCAL->ld = NIL;        /* no more lock fd */
  896.     unlink (LOCAL->lname);    /* delete it */
  897.     fs_give ((void **) &LOCAL->lname);
  898.   }
  899.                 /* make sure it is alright to do this at all */
  900.   if (LOCAL && LOCAL->ld && !stream->lock) {
  901.                 /* get current mailbox size */
  902.     stat (LOCAL->name,&sbuf);    /* parse if mailbox changed */
  903.     if ((sbuf.st_size != LOCAL->filesize) &&
  904.     ((fd = bezerk_parse (stream,lock,LOCK_SH)) >= 0)) {
  905.                 /* unlock mailbox */
  906.       bezerk_unlock (fd,stream,lock);
  907.       mail_unlock (stream);    /* and stream */
  908.     }
  909.   }
  910.   return LOCAL ? T : NIL;    /* return if still alive */
  911. }
  912.  
  913. /* Berkeley mail check mailbox
  914.  * Accepts: MAIL stream
  915.  * No-op for readonly files, since read/writer can expunge it from under us!
  916.  */
  917.  
  918. void bezerk_check (stream)
  919.     MAILSTREAM *stream;
  920. {
  921.   char lock[MAILTMPLEN];
  922.   int fd;
  923.                 /* parse and lock mailbox */
  924.   if (LOCAL && LOCAL->ld && ((fd = bezerk_parse (stream,lock,LOCK_EX)) >= 0)) {
  925.                 /* dump checkpoint if needed */
  926.     if (LOCAL->dirty && bezerk_extend (stream,fd,NIL)) bezerk_save (stream,fd);
  927.                 /* flush locks */
  928.     bezerk_unlock (fd,stream,lock);
  929.     mail_unlock (stream);
  930.   }
  931.   if (LOCAL && LOCAL->ld && !stream->silent) mm_log ("Check completed",NIL);
  932. }
  933.  
  934. /* Berkeley mail expunge mailbox
  935.  * Accepts: MAIL stream
  936.  */
  937.  
  938. void bezerk_expunge (stream)
  939.     MAILSTREAM *stream;
  940. {
  941.   int fd,j;
  942.   long i = 1;
  943.   long n = 0;
  944.   unsigned long recent;
  945.   MESSAGECACHE *elt;
  946.   char *r = "No messages deleted, so no update needed";
  947.   char lock[MAILTMPLEN];
  948.   if (LOCAL && LOCAL->ld) {    /* parse and lock mailbox */
  949.     if ((fd = bezerk_parse (stream,lock,LOCK_EX)) >= 0) {
  950.       recent = stream->recent;    /* get recent now that new ones parsed */
  951.       while ((j = (i<=stream->nmsgs)) && !(elt = mail_elt (stream,i))->deleted)
  952.     i++;            /* find first deleted message */
  953.       if (j) {            /* found one? */
  954.                 /* make sure we can do the worst case thing */
  955.     if (bezerk_extend (stream,fd,"Unable to expunge mailbox")) {
  956.       do {            /* flush deleted messages */
  957.         if ((elt = mail_elt (stream,i))->deleted) {
  958.                 /* if recent, note one less recent message */
  959.           if (elt->recent) --recent;
  960.                 /* flush local cache entry */
  961.           fs_give ((void **) &LOCAL->msgs[i - 1]);
  962.           for (j = i; j < stream->nmsgs; j++)
  963.         LOCAL->msgs[j - 1] = LOCAL->msgs[j];
  964.           LOCAL->msgs[stream->nmsgs - 1] = NIL;
  965.                 /* notify upper levels */
  966.           mail_expunged (stream,i);
  967.           n++;        /* count another expunged message */
  968.         }
  969.         else i++;        /* otherwise try next message */
  970.       } while (i <= stream->nmsgs);
  971.                 /* dump checkpoint of the results */
  972.       bezerk_save (stream,fd);
  973.       sprintf ((r = LOCAL->buf),"Expunged %d messages",n);
  974.     }
  975.       }
  976.                 /* notify upper level, free locks */
  977.       mail_exists (stream,stream->nmsgs);
  978.       mail_recent (stream,recent);
  979.       bezerk_unlock (fd,stream,lock);
  980.       mail_unlock (stream);
  981.     }
  982.   }
  983.   else r = "Expunge ignored on readonly mailbox";
  984.   if (LOCAL && !stream->silent) mm_log (r,NIL);
  985. }
  986.  
  987. /* Berkeley mail copy message(s)
  988.     s;
  989.  * Accepts: MAIL stream
  990.  *        sequence
  991.  *        destination mailbox
  992.  * Returns: T if copy successful, else NIL
  993.  */
  994.  
  995. long bezerk_copy (stream,sequence,mailbox)
  996.     MAILSTREAM *stream;
  997.     char *sequence;
  998.     char *mailbox;
  999. {
  1000.                 /* copy the messages */
  1001.   return (mail_sequence (stream,sequence)) ?
  1002.     bezerk_copy_messages (stream,mailbox) : NIL;
  1003. }
  1004.  
  1005.  
  1006. /* Berkeley mail move message(s)
  1007.     s;
  1008.  * Accepts: MAIL stream
  1009.  *        sequence
  1010.  *        destination mailbox
  1011.  * Returns: T if move successful, else NIL
  1012.  */
  1013.  
  1014. long bezerk_move (stream,sequence,mailbox)
  1015.     MAILSTREAM *stream;
  1016.     char *sequence;
  1017.     char *mailbox;
  1018. {
  1019.   long i;
  1020.   MESSAGECACHE *elt;
  1021.   if (!(mail_sequence (stream,sequence) &&
  1022.     bezerk_copy_messages (stream,mailbox))) return NIL;
  1023.                 /* delete all requested messages */
  1024.   for (i = 1; i <= stream->nmsgs; i++)
  1025.     if ((elt = mail_elt (stream,i))->sequence) {
  1026.       elt->deleted = T;        /* mark message deleted */
  1027.                 /* recalculate Status/X-Status lines */
  1028.       bezerk_update_status (LOCAL->msgs[i - 1]->status,elt);
  1029.       LOCAL->dirty = T;        /* note stream is now dirty */
  1030.     }
  1031.   return T;
  1032. }
  1033.  
  1034. /* Berkeley mail append message from stringstruct
  1035.  * Accepts: MAIL stream
  1036.  *        destination mailbox
  1037.  *        initial flags
  1038.  *        internal date
  1039.  *        stringstruct of messages to append
  1040.  * Returns: T if append successful, else NIL
  1041.  */
  1042.  
  1043. #define BUFLEN 8*MAILTMPLEN
  1044.  
  1045. long bezerk_append (stream,mailbox,flags,date,message)
  1046.     MAILSTREAM *stream;
  1047.     char *mailbox;
  1048.     char *flags;
  1049.     char *date;
  1050.                  STRING *message;
  1051. {
  1052.   struct stat sbuf;
  1053.   int i,fd,ti,zn;
  1054.   char *s,*x,buf[BUFLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
  1055.   time_t tp[2];
  1056.   MESSAGECACHE elt;
  1057.   char c = '\n';
  1058.   long j,n,ok = T;
  1059.   time_t t = time (0);
  1060.   unsigned long size = SIZE (message);
  1061.   short f = bezerk_getflags (stream,flags);
  1062.                 /* parse date if given */
  1063.   if (date && !mail_parse_date (&elt,date)) {
  1064.     sprintf (buf,"Bad date in append: %s",date);
  1065.     mm_log (buf,ERROR);
  1066.     return NIL;
  1067.   }
  1068.                 /* make sure valid mailbox */
  1069.   if (!bezerk_isvalid (mailbox,buf)) switch (errno) {
  1070.   case ENOENT:            /* no such file? */
  1071.     if (strcmp (ucase (strcpy (buf,mailbox)),"INBOX")) {
  1072.       mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
  1073.       return NIL;
  1074.     }
  1075.     else break;
  1076.   case 0:            /* merely empty file? */
  1077.     break;
  1078.   case EINVAL:
  1079.     sprintf (buf,"Invalid Berkeley-format mailbox name: %s",mailbox);
  1080.     mm_log (buf,ERROR);
  1081.     return NIL;
  1082.   default:
  1083.     sprintf (buf,"Not a Berkeley-format mailbox: %s",mailbox);
  1084.     mm_log (buf,ERROR);
  1085.     return NIL;
  1086.   }
  1087.   if ((fd = bezerk_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND|O_CREAT,
  1088.              S_IREAD|S_IWRITE,lock,LOCK_EX)) < 0) {
  1089.     sprintf (buf,"Can't open append mailbox: %s",strerror (errno));
  1090.     mm_log (buf,ERROR);
  1091.     return NIL;
  1092.   }
  1093.  
  1094.   mm_critical (stream);        /* go critical */
  1095.   fstat (fd,&sbuf);        /* get current file size */
  1096.   sprintf (buf,"From %s@%s ",myusername (),mylocalhost ());
  1097.                 /* write the date given */
  1098.   if (date) mail_cdate (buf + strlen (buf),&elt);
  1099.   else strcat (buf,ctime (&t));    /* otherwise write the time now */
  1100.   sprintf (buf + strlen (buf),"Status: %sO\nX-Status: %s%s%s\n",
  1101.        f&fSEEN ? "R" : "",f&fDELETED ? "D" : "",
  1102.        f&fFLAGGED ? "F" : "",f&fANSWERED ? "A" : "");
  1103.   i = strlen (buf);        /* initial buffer space used */
  1104.   while (ok && size--) {    /* copy text, tossing out CR's */
  1105.                 /* if at start of line */
  1106.     if ((c == '\n') && (size > 5)) {
  1107.       n = GETPOS (message);    /* prepend a broket if needed */
  1108.       if ((SNX (message) == 'F') && (SNX (message) == 'r') &&
  1109.       (SNX (message) == 'o') && (SNX (message) == 'm') &&
  1110.       (SNX (message) == ' ')) {
  1111.                 /* always write widget if unconditional */
  1112.     if (bezerk_fromwidget) ok = bezerk_append_putc (fd,buf,&i,'>');
  1113.     else {            /* hairier test, count length of this line */
  1114.       for (j = 6; (j < size) && (SNX (message) != '\n'); j++);
  1115.       if (j < size) {    /* copy line */
  1116.         SETPOS (message,n);    /* restore position */
  1117.         x = s = (char *) fs_get (j + 1);
  1118.         while (j--) if ((c = SNX (message)) != '\015') *x++ = c;
  1119.         *x = '\0';        /* tie off line */
  1120.         VALID (s,x,ti,zn);    /* see if looks like need a widget */
  1121.         if (ti) ok = bezerk_append_putc (fd,buf,&i,'>');
  1122.         fs_give ((void **) &s);
  1123.       }
  1124.     }
  1125.       }
  1126.       SETPOS (message,n);    /* restore position as needed */
  1127.     }
  1128.                 /* copy another character */
  1129.     if ((c = SNX (message)) != '\015') ok = bezerk_append_putc (fd,buf,&i,c);
  1130.   }
  1131.                 /* write trailing newline */
  1132.   if (ok) ok = bezerk_append_putc (fd,buf,&i,'\n');
  1133.   if (!(ok && (ok = (write (fd,buf,i) >= 0)) && (ok = !fsync (fd)))) {
  1134.     sprintf (buf,"Message append failed: %s",strerror (errno));
  1135.     mm_log (buf,ERROR);
  1136.     ftruncate (fd,sbuf.st_size);
  1137.   }
  1138.   tp[0] = sbuf.st_atime;    /* preserve atime */
  1139.   tp[1] = time (0);        /* set mtime to now */
  1140.   utime (file,tp);        /* set the times */
  1141.   bezerk_unlock (fd,NIL,lock);    /* unlock and close mailbox */
  1142.   mm_nocritical (stream);    /* release critical */
  1143.   return ok;            /* return success */
  1144. }
  1145.  
  1146. /* Berkeley mail append character
  1147.  * Accepts: file descriptor
  1148.  *        output buffer
  1149.  *        pointer to current size of output buffer
  1150.  *        character to append
  1151.  * Returns: T if append successful, else NIL
  1152.  */
  1153.  
  1154. long bezerk_append_putc (fd,s,i,c)
  1155.     int fd;
  1156.     char *s;
  1157.     int *i;
  1158.     char c;
  1159. {
  1160.   s[(*i)++] = c;
  1161.   if (*i == BUFLEN) {        /* dump if buffer filled */
  1162.     if (write (fd,s,*i) < 0) return NIL;
  1163.     *i = 0;            /* reset */
  1164.   }
  1165.   return T;
  1166. }
  1167.  
  1168. /* Berkeley garbage collect stream
  1169.  * Accepts: Mail stream
  1170.  *        garbage collection flags
  1171.  */
  1172.  
  1173. void bezerk_gc (stream,gcflags)
  1174.     MAILSTREAM *stream;
  1175.     long gcflags;
  1176. {
  1177.   /* nothing here for now */
  1178. }
  1179.  
  1180. /* Internal routines */
  1181.  
  1182.  
  1183. /* Berkeley mail abort stream
  1184.  * Accepts: MAIL stream
  1185.  */
  1186.  
  1187. void bezerk_abort (stream)
  1188.     MAILSTREAM *stream;
  1189. {
  1190.   long i;
  1191.   if (LOCAL) {            /* only if a file is open */
  1192.     if (LOCAL->name) fs_give ((void **) &LOCAL->name);
  1193.     if (LOCAL->ld) {        /* have a mailbox lock? */
  1194.       flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
  1195.       close (LOCAL->ld);    /* close the lock file */
  1196.       unlink (LOCAL->lname);    /* and delete it */
  1197.     }
  1198.     fs_give ((void **) &LOCAL->lname);
  1199.     if (LOCAL->msgs) {        /* free local cache */
  1200.       for (i = 0; i < stream->nmsgs; ++i) fs_give ((void **) &LOCAL->msgs[i]);
  1201.       fs_give ((void **) &LOCAL->msgs);
  1202.     }
  1203.                 /* free local text buffers */
  1204.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  1205.                 /* nuke the local data */
  1206.     fs_give ((void **) &stream->local);
  1207.     stream->dtb = NIL;        /* log out the DTB */
  1208.   }
  1209. }
  1210.  
  1211. /* Berkeley open and lock mailbox
  1212.  * Accepts: file name to open/lock
  1213.  *        file open mode
  1214.  *        destination buffer for lock file name
  1215.  *        type of locking operation (LOCK_SH or LOCK_EX)
  1216.  */
  1217.  
  1218. int bezerk_lock (file,flags,mode,lock,op)
  1219.     char *file;
  1220.     int flags;
  1221.     int mode;
  1222.     char *lock;
  1223.     int op;
  1224. {
  1225.   int fd,ld,j;
  1226.   int i = LOCKTIMEOUT * 60 - 1;
  1227.   char hitch[MAILTMPLEN],tmp[MAILTMPLEN];
  1228.   time_t t;
  1229.   struct stat sb;
  1230.                 /* build lock filename */
  1231.   if (chk_notsymlink (strcat (dummy_file (lock,file),".lock"),NIL)) do {
  1232.     t = time (0);        /* get the time now */
  1233. #ifdef NFSKLUDGE
  1234.   /* SUN-OS had an NFS, As kludgy as an albatross;
  1235.    * And everywhere that it was installed, It was a total loss.  -- MRC 9/25/91
  1236.    */
  1237.                 /* build hitching post file name */
  1238.     sprintf (hitch,"%s.%d.%d.",lock,time (0),getpid ());
  1239.     j = strlen (hitch);        /* append local host name */
  1240.     gethostname (hitch + j,(MAILTMPLEN - j) - 1);
  1241.                 /* try to get hitching-post file */
  1242.     if ((ld = open (hitch,O_WRONLY|O_CREAT|O_EXCL,
  1243.             (int) mail_parameters (NIL,GET_LOCKPROTECTION,NIL))) < 0) {
  1244.       sprintf (tmp,"Error creating %s: %s",hitch,strerror (errno));
  1245.       switch (errno) {        /* what happened? */
  1246.       case EEXIST:        /* file already exists? */
  1247.     break;            /* oops, just try again */
  1248.       case EACCES:        /* protection failure */
  1249.                 /* try again if file exists(?) */
  1250.     if (!stat (hitch,&sb)) break;
  1251.                 /* punt silently if paranoid site */
  1252.     if (mail_parameters (NIL,GET_LOCKEACCESERROR,NIL))
  1253.       default:            /* some other error */
  1254.       mm_log (tmp,WARN);    /* this is probably not good */
  1255.     *lock = '\0';        /* give up on lock file */
  1256.     break;
  1257.       }
  1258.     }
  1259.     else {            /* got a hitching-post */
  1260.                 /* make sure others can break the lock */
  1261.       chmod (hitch,(int) mail_parameters (NIL,GET_LOCKPROTECTION,NIL));
  1262.       close (ld);        /* close the hitching-post */
  1263.       link (hitch,lock);    /* tie hitching-post to lock, ignore failure */
  1264.       stat (hitch,&sb);        /* get its data */
  1265.       unlink (hitch);        /* flush hitching post */
  1266.       /* If link count .ne. 2, hitch failed.  Set ld to -1 as if open() failed
  1267.      so we try again.  If extant lock file and time now is .gt. file time
  1268.      plus timeout interval, flush the lock so can win next time around. */
  1269.       if ((ld = (sb.st_nlink != 2) ? -1 : 0) && (!stat (lock,&sb)) &&
  1270.       (t > sb.st_ctime + LOCKTIMEOUT * 60)) unlink (lock);
  1271.     }
  1272.  
  1273. #else
  1274.   /* This works on modern Unix systems which are not afflicted with NFS mail.
  1275.    * "Modern" means that O_EXCL works.  I think that NFS mail is a terrible
  1276.    * idea -- that's what IMAP is for -- but some people insist upon losing...
  1277.    */
  1278.                 /* try to get the lock */
  1279.     if ((ld = open (lock,O_WRONLY|O_CREAT|O_EXCL,
  1280.             (int) mail_parameters (NIL,GET_LOCKPROTECTION,NIL))) < 0)
  1281.       switch (errno) {        /* what happened? */
  1282.       case EEXIST:        /* if extant and old, grab it for ourselves */
  1283.     if ((!stat (lock,&sb)) && t > sb.st_ctime + LOCKTIMEOUT * 60)
  1284.       ld = open (lock,O_WRONLY|O_CREAT,
  1285.              (int) mail_parameters (NIL,GET_LOCKPROTECTION,NIL));
  1286.     break;
  1287.       case EACCES:        /* protection fail, ignore if non-ex or old */
  1288.     if (!mail_parameters (NIL,GET_LOCKEACCESERROR,NIL)) {
  1289.       if (stat (lock,&sb) || (t > sb.st_ctime + LOCKTIMEOUT * 60))
  1290.         *lock = '\0';    /* assume no world write mail spool dir */
  1291.       break;
  1292.     }
  1293.       default:            /* some other failure */
  1294.     sprintf (tmp,"Error creating %s: %s",lock,strerror (errno));
  1295.     mm_log (tmp,WARN);    /* this is probably not good */
  1296.     *lock = '\0';        /* don't use lock files */
  1297.     break;
  1298.       }
  1299.     if (ld >= 0) {        /* if made a lock file */
  1300.                 /* make sure others can break the lock */
  1301.       chmod (lock,(int) mail_parameters (NIL,GET_LOCKPROTECTION,NIL));
  1302.       close (ld);        /* close the lock file */
  1303.     }
  1304. #endif
  1305.     if ((ld < 0) && *lock) {    /* if failed to make lock file and retry OK */
  1306.       if (!(i%15)) {
  1307.     sprintf (tmp,"Mailbox %s is locked, will override in %d seconds...",
  1308.          file,i);
  1309.     mm_log (tmp,WARN);
  1310.       }
  1311.       sleep (1);        /* wait 1 second before next try */
  1312.     }
  1313.   } while (i-- && ld < 0 && *lock);
  1314.                 /* open file */
  1315.   if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
  1316.   else {            /* open failed */
  1317.     j = errno;            /* preserve error code */
  1318.     if (*lock) unlink (lock);    /* flush the lock file if any */
  1319.     errno = j;            /* restore error code */
  1320.   }
  1321.   return fd;
  1322. }
  1323.  
  1324. /* Berkeley unlock and close mailbox
  1325.  * Accepts: file descriptor
  1326.  *        (optional) mailbox stream to check atime/mtime
  1327.  *        (optional) lock file name
  1328.  */
  1329.  
  1330. void bezerk_unlock (fd,stream,lock)
  1331.     int fd;
  1332.     MAILSTREAM *stream;
  1333.     char *lock;
  1334. {
  1335.   struct stat sbuf;
  1336.   time_t tp[2];
  1337.   fstat (fd,&sbuf);        /* get file times */
  1338.                 /* if stream and csh would think new mail */
  1339.   if (stream && (sbuf.st_atime <= sbuf.st_mtime)) {
  1340.     tp[0] = time (0);        /* set atime to now */
  1341.                 /* set mtime to (now - 1) if necessary */
  1342.     tp[1] = tp[0] > sbuf.st_mtime ? sbuf.st_mtime : tp[0] - 1;
  1343.                 /* set the times, note change */
  1344.     if (!utime (LOCAL->name,tp)) LOCAL->filetime = tp[1];
  1345.   }
  1346.   flock (fd,LOCK_UN);        /* release flock'ers */
  1347.   close (fd);            /* close the file */
  1348.                 /* flush the lock file if any */
  1349.   if (lock && *lock) unlink (lock);
  1350. }
  1351.  
  1352. /* Berkeley mail parse and lock mailbox
  1353.  * Accepts: MAIL stream
  1354.  *        space to write lock file name
  1355.  *        type of locking operation
  1356.  * Returns: file descriptor if parse OK, mailbox is locked shared
  1357.  *        -1 if failure, stream aborted
  1358.  */
  1359.  
  1360. int bezerk_parse (stream,lock,op)
  1361.     MAILSTREAM *stream;
  1362.     char *lock;
  1363.     int op;
  1364. {
  1365.   int fd;
  1366.   long delta,i,j,is,is1;
  1367.   char c = '\n',*s,*s1,*t = NIL,*e;
  1368.   int ti = 0,zn = 0;
  1369.   long nmsgs = stream->nmsgs;
  1370.   long newcnt = 0;
  1371.   struct stat sbuf;
  1372.   STRING bs;
  1373.   MESSAGECACHE *elt;
  1374.   FILECACHE *m = NIL,*n = NIL;
  1375.   mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
  1376.   mail_lock (stream);        /* guard against recursion or pingers */
  1377.                 /* open and lock mailbox (shared OK) */
  1378.   if ((fd = bezerk_lock (LOCAL->name,LOCAL->ld ? O_RDWR : O_RDONLY,NIL,
  1379.              lock,op)) < 0) {
  1380.     sprintf (LOCAL->buf,"Mailbox open failed, aborted: %s",strerror (errno));
  1381.     mm_log (LOCAL->buf,ERROR);
  1382.     bezerk_abort (stream);
  1383.     mail_unlock (stream);
  1384.     return -1;
  1385.   }
  1386.   fstat (fd,&sbuf);        /* get status */
  1387.                 /* calculate change in size */
  1388.   if ((delta = sbuf.st_size - LOCAL->filesize) < 0) {
  1389.     sprintf (LOCAL->buf,"Mailbox shrank from %d to %d bytes, aborted",
  1390.          LOCAL->filesize,sbuf.st_size);
  1391.     mm_log (LOCAL->buf,ERROR);    /* this is pretty bad */
  1392.     bezerk_unlock (fd,stream,lock);
  1393.     bezerk_abort (stream);
  1394.     mail_unlock (stream);
  1395.     return -1;
  1396.   }
  1397.  
  1398.   else if (delta) {        /* get to that position in the file */
  1399.     lseek (fd,LOCAL->filesize,L_SET);
  1400.     s = s1 = LOCAL->buf;    /* initial read-in location */
  1401.     i = 0;            /* initial unparsed read-in count */
  1402.     do {
  1403.       i = min (CHUNK,delta);    /* calculate read-in size */
  1404.                 /* increase the read-in buffer if necessary */
  1405.       if ((j = i + (s1 - s)) >= LOCAL->buflen) {
  1406.     is = s - LOCAL->buf;    /* note former start of message position */
  1407.     is1 = s1 - LOCAL->buf;    /* and start of new data position */
  1408.     if (s1 - s) fs_resize ((void **) &LOCAL->buf,(LOCAL->buflen = j) + 1);
  1409.     else {            /* fs_resize would do an unnecessary copy */
  1410.       fs_give ((void **) &LOCAL->buf);
  1411.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = j) + 1);
  1412.     }
  1413.     s = LOCAL->buf + is;    /* new start of message */
  1414.     s1 = LOCAL->buf + is1;    /* new start of new data */
  1415.       }
  1416.       s1[i] = '\0';        /* tie off chunk */
  1417.       if (read (fd,s1,i) < 0) {    /* read a chunk of new text */
  1418.     sprintf (LOCAL->buf,"Error reading mail file: %s",strerror (errno));
  1419.     mm_log (LOCAL->buf,ERROR);
  1420.     bezerk_unlock (fd,stream,lock);
  1421.     bezerk_abort (stream);
  1422.     mail_unlock (stream);
  1423.     return -1;
  1424.       }
  1425.       delta -= i;        /* account for data read in */
  1426.       if (c) {            /* validate newly-appended data */
  1427.                 /* skip leading whitespace */
  1428.     while ((*s == '\n') || (*s == ' ') || (*s == '\t')) {
  1429.       c = *s++;        /* yes, skip the damn thing */
  1430.       s1++;
  1431.       if (!--i) break;    /* only whitespace was appended?? */
  1432.     }
  1433.                 /* see new data is valid */
  1434.     if (c == '\n') VALID (s,t,ti,zn);
  1435.         if (!ti) {        /* invalid data? */
  1436.       char tmp[MAILTMPLEN];
  1437.       sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s",
  1438.            s);
  1439.       mm_log (tmp,ERROR);
  1440.       bezerk_unlock (fd,stream,lock);
  1441.       bezerk_abort (stream);
  1442.       mail_unlock (stream);
  1443.       return -1;
  1444.     }
  1445.     c = NIL;        /* don't need to do this again */
  1446.       }
  1447.  
  1448.                 /* found end of message or end of data? */
  1449.       while ((e = bezerk_eom (s,s1,i)) || !delta) {
  1450.     nmsgs++;        /* yes, have a new message */
  1451.                 /* calculate message length */
  1452.     j = ((e ? e : s1 + i) - s) - 1;
  1453.     if (m) {        /* new cache needed, have previous data? */
  1454.       n->header = (char *) fs_get (sizeof (FILECACHE) + j + 3);
  1455.       n = (FILECACHE *) n->header;
  1456.     }
  1457.     else m = n = (FILECACHE *) fs_get (sizeof (FILECACHE) + j + 3);
  1458.                 /* copy message data */
  1459.     memcpy (n->internal,s,j);
  1460.                 /* ensure ends with newline */
  1461.     if (s[j-1] != '\n') n->internal[j++] = '\n';
  1462.     n->internal[j] = '\0';
  1463.     n->header = NIL;    /* initially no link */
  1464.     n->headersize = j;    /* stash away buffer length */
  1465.     if (e) {        /* saw end of message? */
  1466.       i -= e - s1;        /* new unparsed data count */
  1467.       s = s1 = e;        /* advance to new message */
  1468.     }
  1469.     else break;        /* else punt this loop */
  1470.       }
  1471.       if (delta) {        /* end of message not found? */
  1472.     s1 += i;        /* end of unparsed data */
  1473.     if (s != LOCAL->buf){    /* message doesn't begin at buffer? */
  1474.       i = s1 - s;        /* length of message so far */
  1475.       memmove (LOCAL->buf,s,i);
  1476.       s = LOCAL->buf;    /* message now starts at buffer origin */
  1477.       s1 = s + i;        /* calculate new end of unparsed data */
  1478.     }
  1479.       }
  1480.     } while (delta);        /* until nothing more new to read */
  1481.   }
  1482.   else {            /* no change, don't babble if never got time */
  1483.     if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
  1484.       mm_log ("New mailbox modification time but apparently no changes",WARN);
  1485.   }
  1486.   (*mc) (stream,nmsgs,CH_SIZE);    /* expand the primary cache */
  1487.   if (nmsgs>=LOCAL->cachesize) {/* need to expand cache? */
  1488.                 /* number of messages plus room to grow */
  1489.     LOCAL->cachesize = nmsgs + CACHEINCREMENT;
  1490.     if (LOCAL->msgs)        /* resize if already have a cache */
  1491.       fs_resize ((void **) &LOCAL->msgs,LOCAL->cachesize*sizeof (FILECACHE *));
  1492.     else LOCAL->msgs =        /* create new cache */
  1493.       (FILECACHE **) fs_get (LOCAL->cachesize * sizeof (FILECACHE *));
  1494.   }
  1495.   if (LOCAL->buflen > CHUNK) {    /* maybe move where the buffer is in memory*/
  1496.     fs_give ((void **) &LOCAL->buf);
  1497.     LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNK) + 1);
  1498.   }
  1499.  
  1500.   for (i = stream->nmsgs, n = m; i < nmsgs; i++) {
  1501.     LOCAL->msgs[i] = m = n;    /* set cache, and next cache pointer */
  1502.     n = (FILECACHE *) n->header;
  1503.     /* This is a bugtrap for bogons in the new message cache, which may happen
  1504.      * if memory is corrupted.  Note that in the case of a totally empty
  1505.      * message, a newline is appended and counts adjusted.
  1506.      */
  1507.     ti = NIL;            /* valid header not found */
  1508.     if (s = m->internal) VALID (s,t,ti,zn);
  1509.     if (!ti) fatal ("Bogus entry in new cache list");
  1510.                 /* pointer to message header */
  1511.     if (s = strchr (t++,'\n')) m->header = ++s;
  1512.     else {            /* probably totally empty message */
  1513.       strcat (t-1,"\n");    /* append newline */
  1514.       m->headersize++;        /* adjust count */
  1515.       m->header = s = strchr (t-1,'\n') + 1;
  1516.     }
  1517.     m->headersize -= m->header - m->internal;
  1518.     m->body = NIL;        /* assume no body as yet */
  1519.     m->bodysize = 0;
  1520.                 /* instantiate elt */
  1521.     (elt = mail_elt (stream,i+1))->valid = T;
  1522.     newcnt++;            /* assume recent by default */
  1523.     elt->recent = T;
  1524.                 /* calculate initial Status/X-Status lines */
  1525.     bezerk_update_status (m->status,elt);
  1526.                 /* generate plausable IMAPish date string */
  1527.     LOCAL->buf[2] = LOCAL->buf[6] = LOCAL->buf[20] = '-';
  1528.     LOCAL->buf[11] = ' ';
  1529.     LOCAL->buf[14] = LOCAL->buf[17] = ':';
  1530.                 /* dd */
  1531.     LOCAL->buf[0] = t[ti - 3]; LOCAL->buf[1] = t[ti - 2];
  1532.                 /* mmm */
  1533.     LOCAL->buf[3] = t[ti - 7]; LOCAL->buf[4] = t[ti - 6];
  1534.     LOCAL->buf[5] = t[ti - 5];
  1535.                 /* hh */
  1536.     LOCAL->buf[12] = t[ti]; LOCAL->buf[13] = t[ti + 1];
  1537.                 /* mm */
  1538.     LOCAL->buf[15] = t[ti + 3]; LOCAL->buf[16] = t[ti + 4];
  1539.     if (t[ti += 5] == ':') {    /* ss if present */
  1540.       LOCAL->buf[18] = t[++ti];
  1541.       LOCAL->buf[19] = t[++ti];
  1542.       ti++;            /* move to space */
  1543.     }
  1544.     else LOCAL->buf[18] = LOCAL->buf[19] = '0';
  1545.                 /* yy -- advance over timezone if necessary */
  1546.     if (zn == ++ti) ti += (((t[zn] == '+') || (t[zn] == '-')) ? 6 : 4);
  1547.     LOCAL->buf[7] = t[ti]; LOCAL->buf[8] = t[ti + 1];
  1548.     LOCAL->buf[9] = t[ti + 2]; LOCAL->buf[10] = t[ti + 3];
  1549.  
  1550.                 /* zzz */
  1551.     t = zn ? (t + zn) : "LCL";
  1552.     LOCAL->buf[21] = *t++; LOCAL->buf[22] = *t++; LOCAL->buf[23] = *t++;
  1553.     if ((LOCAL->buf[21] != '+') && (LOCAL->buf[21] != '-'))
  1554.       LOCAL->buf[24] = '\0';
  1555.     else {            /* numeric time zone */
  1556.       LOCAL->buf[24] = *t++; LOCAL->buf[25] = *t++;
  1557.       LOCAL->buf[26] = '\0'; LOCAL->buf[20] = ' ';
  1558.     }
  1559.                 /* set internal date */
  1560.     if (!mail_parse_date (elt,LOCAL->buf)) mm_log ("Unparsable date",WARN);
  1561.     e = NIL;            /* no status stuff yet */
  1562.     do switch (*(t = s)) {    /* look at header lines */
  1563.     case '\n':            /* end of header */
  1564.       m->body = ++s;        /* start of body is here */
  1565.       j = m->body - m->header;    /* new header size */
  1566.                 /* calculate body size */
  1567.       if (m->headersize >= j) m->bodysize = m->headersize - j;
  1568.       if (e) {            /* saw status poop? */
  1569.     *e++ = '\n';        /* patch in trailing newline */
  1570.     m->headersize = e - m->header;
  1571.       }
  1572.       else m->headersize = j;    /* set header size */
  1573.       s = NIL;            /* don't scan any further */
  1574.       break;
  1575.     case '\0':            /* end of message */
  1576.       if (e) {            /* saw status poop? */
  1577.     *e++ = '\n';        /* patch in trailing newline */
  1578.     m->headersize = e - m->header;
  1579.       }
  1580.       break;
  1581.  
  1582.     case 'X':            /* possible X-Status: line */
  1583.       if (s[1] == '-' && s[2] == 'S' && s[3] == 't' && s[4] == 'a' &&
  1584.       s[5] == 't' && s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2;
  1585.     case 'S':            /* possible Status: line */
  1586.       if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' &&
  1587.       s[4] == 'u' && s[5] == 's' && s[6] == ':') {
  1588.     if (!e) e = t;        /* note deletion point */
  1589.     s += 6;            /* advance to status flags */
  1590.     do switch (*s++) {    /* parse flags */
  1591.     case 'R':        /* message read */
  1592.       elt->seen = T;
  1593.       break;
  1594.     case 'O':        /* message old */
  1595.       if (elt->recent) {    /* don't do this more than once! */
  1596.         elt->recent = NIL;    /* not recent any longer... */
  1597.         newcnt--;
  1598.       }
  1599.       break;
  1600.     case 'D':        /* message deleted */
  1601.       elt->deleted = T;
  1602.       break;
  1603.     case 'F':        /* message flagged */
  1604.       elt ->flagged = T;
  1605.       break;
  1606.     case 'A':        /* message answered */
  1607.       elt ->answered = T;
  1608.       break;
  1609.     default:        /* some other crap */
  1610.       break;
  1611.     } while (*s && *s != '\n');
  1612.                 /* recalculate Status/X-Status lines */
  1613.     bezerk_update_status (m->status,elt);
  1614.     break;            /* all done */
  1615.       }
  1616.                 /* otherwise fall into default case */
  1617.  
  1618.     default:            /* anything else is uninteresting */
  1619.       if (e) {            /* have status stuff to worry about? */
  1620.     j = s - e;        /* yuck!!  calculate size of delete area */
  1621.                 /* blat remaining number of bytes down */
  1622.     memmove (e,s,m->header + m->headersize - s);
  1623.     m->headersize -= j;    /* update for new size */
  1624.     s = e;            /* back up pointer */
  1625.     e = NIL;        /* no more delete area */
  1626.                 /* tie off old cruft */
  1627.     *(m->header + m->headersize) = '\0';
  1628.       }
  1629.       break;
  1630.     } while (s && (s = strchr (s,'\n')) && s++);
  1631.                 /* get size including CR's  */
  1632.     INIT (&bs,mail_string,(void *) m->header,m->headersize);
  1633.     elt->rfc822_size = strcrlflen (&bs);
  1634.     INIT (&bs,mail_string,(void *) m->body,m->bodysize);
  1635.     elt->rfc822_size += strcrlflen (&bs);
  1636.   }
  1637.   if (n) fatal ("Cache link-list inconsistency");
  1638.   while (i < LOCAL->cachesize) LOCAL->msgs[i++] = NIL;
  1639.                 /* update parsed file size and time */
  1640.   LOCAL->filesize = sbuf.st_size;
  1641.   LOCAL->filetime = sbuf.st_mtime;
  1642.   mail_exists (stream,nmsgs);    /* notify upper level of new mailbox size */
  1643.   mail_recent (stream,stream->recent + newcnt);
  1644.   if (newcnt) LOCAL->dirty = T;    /* mark dirty so O flags are set */
  1645.   return fd;            /* return the winnage */
  1646. }
  1647.  
  1648. /* Berkeley search for end of message
  1649.  * Accepts: start of message
  1650.  *        start of new data
  1651.  *        size of new data
  1652.  * Returns: pointer to start of new message if one found
  1653.  */
  1654.  
  1655. #define Word unsigned long
  1656.  
  1657. char *bezerk_eom (som,sod,i)
  1658.     char *som;
  1659.     char *sod;
  1660.     long i;
  1661. {
  1662.   char *s = (sod > som) ? sod - 1 : sod;
  1663.   char *s1,*t;
  1664.   int ti,zn;
  1665.   union {
  1666.     unsigned long wd;
  1667.     char ch[9];
  1668.   } wdtest;
  1669.   while ((s > som) && *s-- != '\n');
  1670.   if (i > 50) {            /* don't do fast search if very few bytes */
  1671.                 /* constant for word testing */
  1672.     strcpy (wdtest.ch,"AAAA1234");
  1673.     if(wdtest.wd == 0x41414141){/* not a 32-bit word machine? */
  1674.       register Word m = 0x0a0a0a0a;
  1675.                 /* any characters before word boundary? */
  1676.       while ((long) s & 3) if (*s++ == '\n') {
  1677.     VALID (s,t,ti,zn);
  1678.     if (ti) return s;
  1679.       }
  1680.       i = (sod + i) - s;    /* total number of tries */
  1681.       do {            /* fast search for newline */
  1682.     if ((0x80808080 & (0x01010101 + (0x7f7f7f7f & ~(m ^ *(Word *) s)))) &&
  1683.                 /* find rightmost newline in word */
  1684.         ((*(s1 = s + 3) == '\n') || (*--s1 == '\n') ||
  1685.          (*--s1 == '\n') || (*--s1 == '\n'))) {
  1686.       s1++;            /* skip past newline */
  1687.       VALID (s1,t,ti,zn);    /* see if valid From line */
  1688.       if (ti) return s1;
  1689.     }
  1690.     s += 4;            /* try next word */
  1691.     i -= 4;            /* count a word checked */
  1692.       } while (i > 24);        /* continue until end of plausible string */
  1693.     }
  1694.   }
  1695.                 /* slow search - still plausible string? */
  1696.   while (i-- > 24) if (*s++ == '\n') {
  1697.     VALID (s,t,ti,zn);        /* if found start of message... */
  1698.     if (ti) return s;        /* return that pointer */
  1699.   }
  1700.   return NIL;
  1701. }
  1702.  
  1703. /* Berkeley extend mailbox to reserve worst-case space for expansion
  1704.  * Accepts: MAIL stream
  1705.  *        file descriptor
  1706.  *        error string
  1707.  * Returns: T if extend OK and have gone critical, NIL if should abort
  1708.  */
  1709.  
  1710. int bezerk_extend (stream,fd,error)
  1711.     MAILSTREAM *stream;
  1712.     int fd;
  1713.     char *error;
  1714. {
  1715.   struct stat sbuf;
  1716.   MESSAGECACHE *elt;
  1717.   FILECACHE *m;
  1718.   char tmp[MAILTMPLEN];
  1719.   int i,ok;
  1720.   long f;
  1721.   char *s;
  1722.   int retry;
  1723.                 /* calculate estimated size of mailbox */
  1724.   for (i = 0,f = 0; i < stream->nmsgs;) {
  1725.     m = LOCAL->msgs[i];        /* get cache pointer */
  1726.     elt = mail_elt (stream,++i);/* get elt, increment message */
  1727.                 /* if not expunging, or not deleted */
  1728.     if (!(error && elt->deleted))
  1729.       f += (m->header - m->internal) + m->headersize + m->bodysize + 1 +
  1730.     sizeof (STATUS) - (elt->seen+elt->deleted+elt->flagged+elt->answered);
  1731.   }
  1732.   mm_critical (stream);        /* go critical */
  1733.                 /* return now if file large enough */
  1734.   if (f <= LOCAL->filesize) return T;
  1735.   s = (char *) fs_get (f -= LOCAL->filesize);
  1736.   memset (s,0,f);        /* get a block of nulls */
  1737.                 /* get to end of file */
  1738.   lseek (fd,LOCAL->filesize,L_SET);
  1739.   do {
  1740.     retry = NIL;        /* no retry yet */
  1741.     if (!(ok = (write (fd,s,f) >= 0))) {
  1742.       i = errno;        /* note error before doing ftruncate */
  1743.                 /* restore prior file size */
  1744.       ftruncate (fd,LOCAL->filesize);
  1745.       fsync (fd);        /* is this necessary? */
  1746.       fstat (fd,&sbuf);        /* now get updated file time */
  1747.       LOCAL->filetime = sbuf.st_mtime;
  1748.                 /* punt if that's what main program wants */
  1749.       if (mm_diskerror (stream,i,NIL)) {
  1750.     mm_nocritical (stream);    /* exit critical */
  1751.     sprintf (tmp,"%s: %s",error ? error : "Unable to update mailbox",
  1752.          strerror (i));
  1753.     mm_notify (stream,tmp,WARN);
  1754.       }
  1755.       else retry = T;        /* set to retry */
  1756.     }
  1757.   } while (retry);        /* repeat if need to try again */
  1758.   fs_give ((void **) &s);    /* flush buffer of nulls */
  1759.   return ok;            /* return status */
  1760. }
  1761.  
  1762. /* Berkeley save mailbox
  1763.  * Accepts: MAIL stream
  1764.  *        mailbox file descriptor
  1765.  *
  1766.  * Mailbox must be readwrite and locked for exclusive access.
  1767.  */
  1768.  
  1769. void bezerk_save (stream,fd)
  1770.     MAILSTREAM *stream;
  1771.     int fd;
  1772. {
  1773.   struct stat sbuf;
  1774.   long i;
  1775.   int e;
  1776.   int retry;
  1777.   do {                /* restart point if failure */
  1778.     retry = NIL;        /* no need to retry yet */
  1779.                 /* start at beginning of file */
  1780.     lseek (fd,LOCAL->filesize = 0,L_SET);
  1781.                 /* loop through all messages */
  1782.     for (i = 1; i <= stream->nmsgs; i++) {
  1783.                 /* write message */
  1784.       if ((e = bezerk_write_message (fd,LOCAL->msgs[i-1])) < 0) {
  1785.     sprintf (LOCAL->buf,"Mailbox rewrite error: %s",strerror (e = errno));
  1786.     mm_log (LOCAL->buf,WARN);
  1787.     mm_diskerror (stream,e,T);
  1788.     retry = T;        /* must retry */
  1789.     break;            /* abort this particular try */
  1790.       }
  1791.       else LOCAL->filesize += e;/* count these bytes in data */
  1792.     }
  1793.     if (fsync (fd)) {        /* make sure the updates take */
  1794.       sprintf (LOCAL->buf,"Unable to sync mailbox: %s",strerror (e = errno));
  1795.       mm_log (LOCAL->buf,WARN);
  1796.       mm_diskerror (stream,e,T);
  1797.       retry = T;        /* oops */
  1798.     }
  1799.   } while (retry);        /* repeat if need to try again */
  1800.   ftruncate(fd,LOCAL->filesize);/* nuke any cruft after that */
  1801.   fsync (fd);            /* is this necessary? */
  1802.   fstat (fd,&sbuf);        /* now get updated file time */
  1803.   LOCAL->filetime = sbuf.st_mtime;
  1804.   LOCAL->dirty = NIL;        /* stream no longer dirty */
  1805.   mm_nocritical (stream);    /* exit critical */
  1806. }
  1807.  
  1808. /* Berkeley copy messages
  1809.  * Accepts: MAIL stream
  1810.  *        mailbox name
  1811.  * Returns: T if copy successful else NIL
  1812.  */
  1813.  
  1814. int bezerk_copy_messages (stream,mailbox)
  1815.     MAILSTREAM *stream;
  1816.     char *mailbox;
  1817. {
  1818.   char file[MAILTMPLEN],lock[MAILTMPLEN];
  1819.   struct stat sbuf;
  1820.   int fd;
  1821.   time_t tp[2];
  1822.   long i;
  1823.   int ok = T;
  1824.                 /* make sure valid mailbox */
  1825.   if (!bezerk_isvalid (mailbox,file)) switch (errno) {
  1826.   case ENOENT:            /* no such file? */
  1827.     mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
  1828.     return NIL;
  1829.   case 0:            /* merely empty file? */
  1830.     break;
  1831.   case EINVAL:
  1832.     sprintf (LOCAL->buf,"Invalid Berkeley-format mailbox name: %s",mailbox);
  1833.     mm_log (LOCAL->buf,ERROR);
  1834.     return NIL;
  1835.   default:
  1836.     sprintf (LOCAL->buf,"Not a Berkeley-format mailbox: %s",mailbox);
  1837.     mm_log (LOCAL->buf,ERROR);
  1838.     return NIL;
  1839.   }
  1840.   if ((fd = bezerk_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND|O_CREAT,
  1841.              S_IREAD|S_IWRITE,lock,LOCK_EX)) < 0) {
  1842.     sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
  1843.     mm_log (LOCAL->buf,ERROR);
  1844.     return NIL;
  1845.   }
  1846.   mm_critical (stream);        /* go critical */
  1847.   fstat (fd,&sbuf);        /* get current file size */
  1848.                 /* write all requested messages to mailbox */
  1849.   for (i = 1; ok && i <= stream->nmsgs; i++)
  1850.                 /* copy message if selected */
  1851.     if (mail_elt (stream,i)->sequence &&
  1852.     (bezerk_write_message (fd,LOCAL->msgs[i - 1]) < 0)) {
  1853.       sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
  1854.       mm_log (LOCAL->buf,ERROR);
  1855.       ftruncate (fd,sbuf.st_size);
  1856.       ok = NIL;
  1857.       break;
  1858.     }
  1859.   if (fsync (fd)) {        /* force out the update */
  1860.     sprintf (LOCAL->buf,"Message copy sync failed: %s",strerror (errno));
  1861.     mm_log (LOCAL->buf,ERROR);
  1862.     ftruncate (fd,sbuf.st_size);
  1863.     ok = NIL;            /* oops */
  1864.   }
  1865.   tp[0] = sbuf.st_atime;    /* preserve atime */
  1866.   tp[1] = time (0);        /* set mtime to now */
  1867.   utime (file,tp);        /* set the times */
  1868.   bezerk_unlock (fd,NIL,lock);    /* unlock and close mailbox */
  1869.   mm_nocritical (stream);    /* release critical */
  1870.   return ok;            /* return whether or not succeeded */
  1871. }
  1872.  
  1873. /* Berkeley write message to mailbox
  1874.  * Accepts: file descriptor
  1875.  *        local cache for this message
  1876.  * Returns: number of bytes written or -1 if error
  1877.  *
  1878.  * This routine is the reason why the local cache has a copy of the status.
  1879.  * We can be called to dump out the mailbox as part of a stream recycle, since
  1880.  * we don't write out the mailbox when flags change and hence an update may be
  1881.  * needed.  However, at this point the elt has already become history, so we
  1882.  * can't use any information other than what is local to us.
  1883.  */
  1884.  
  1885. int bezerk_write_message (fd,m)
  1886.     int fd;
  1887.     FILECACHE *m;
  1888. {
  1889.   struct iovec iov[16];
  1890.   int i = 0;
  1891.   iov[i].iov_base = m->internal;/* pointer/counter to headers */
  1892.                 /* length of internal + message headers */
  1893.   iov[i].iov_len = (m->header + m->headersize) - m->internal;
  1894.                 /* suppress extra newline if present */
  1895.   if (((char *) iov[i].iov_base)[iov[i].iov_len - 2] == '\n')
  1896.     iov[i++].iov_len--;
  1897.   else i++;            /* unlikely but... */
  1898.   iov[i].iov_base = m->status;    /* pointer/counter to status */
  1899.   iov[i++].iov_len = strlen (m->status);
  1900.   if (m->bodysize) {        /* only if a non-empty body */
  1901.     iov[i].iov_base = m->body;    /* pointer/counter to text body */
  1902.     iov[i++].iov_len = m->bodysize;
  1903.   }
  1904.   iov[i].iov_base = "\n";    /* pointer/counter to extra newline */
  1905.   iov[i++].iov_len = 1;
  1906.   return writev (fd,iov,i);
  1907. }
  1908.  
  1909. /* Berkeley update status string
  1910.  * Accepts: destination string to write
  1911.  *        message cache entry
  1912.  */
  1913.  
  1914. void bezerk_update_status (status,elt)
  1915.     char *status;
  1916.     MESSAGECACHE *elt;
  1917. {
  1918.   /* This used to be an sprintf(), but thanks to certain cretinous C libraries
  1919.      with horribly slow implementations of sprintf() I had to change it to this
  1920.      mess.  At least it should be fast. */
  1921.   char *t = status + 8;
  1922.   status[0] = 'S'; status[1] = 't'; status[2] = 'a'; status[3] = 't';
  1923.   status[4] = 'u'; status[5] = 's'; status[6] = ':';  status[7] = ' ';
  1924.   if (elt->seen) *t++ = 'R'; *t++ = 'O'; *t++ = '\n';
  1925.   *t++ = 'X'; *t++ = '-'; *t++ = 'S'; *t++ = 't'; *t++ = 'a'; *t++ = 't';
  1926.   *t++ = 'u'; *t++ = 's'; *t++ = ':'; *t++ = ' ';
  1927.   if (elt->deleted) *t++ = 'D'; if (elt->flagged) *t++ = 'F';
  1928.   if (elt->answered) *t++ = 'A';
  1929.   *t++ = '\n'; *t++ = '\n'; *t++ = '\0';
  1930. }
  1931.  
  1932. /* Parse flag list
  1933.  * Accepts: MAIL stream
  1934.  *        flag list as a character string
  1935.  * Returns: flag command list
  1936.  */
  1937.  
  1938.  
  1939. short bezerk_getflags (stream,flag)
  1940.     MAILSTREAM *stream;
  1941.     char *flag;
  1942. {
  1943.   char *t,tmp[MAILTMPLEN],err[MAILTMPLEN];
  1944.   short f = 0;
  1945.   short i,j;
  1946.   if (flag && *flag) {        /* no-op if no flag string */
  1947.                 /* check if a list and make sure valid */
  1948.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  1949.       mm_log ("Bad flag list",ERROR);
  1950.       return NIL;
  1951.     }
  1952.                 /* copy the flag string w/o list construct */
  1953.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  1954.     tmp[j] = '\0';
  1955.     t = ucase (tmp);        /* uppercase only from now on */
  1956.  
  1957.     while (t && *t) {        /* parse the flags */
  1958.       if (*t == '\\') {        /* system flag? */
  1959.     switch (*++t) {        /* dispatch based on first character */
  1960.     case 'S':        /* possible \Seen flag */
  1961.       if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N') i = fSEEN;
  1962.       t += 4;        /* skip past flag name */
  1963.       break;
  1964.     case 'D':        /* possible \Deleted flag */
  1965.       if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  1966.           t[5] == 'E' && t[6] == 'D') i = fDELETED;
  1967.       t += 7;        /* skip past flag name */
  1968.       break;
  1969.     case 'F':        /* possible \Flagged flag */
  1970.       if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  1971.           t[5] == 'E' && t[6] == 'D') i = fFLAGGED;
  1972.       t += 7;        /* skip past flag name */
  1973.       break;
  1974.     case 'A':        /* possible \Answered flag */
  1975.       if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  1976.           t[5] == 'R' && t[6] == 'E' && t[7] == 'D') i = fANSWERED;
  1977.       t += 8;        /* skip past flag name */
  1978.       break;
  1979.     default:        /* unknown */
  1980.       i = 0;
  1981.       break;
  1982.     }
  1983.                 /* add flag to flags list */
  1984.     if (i && ((*t == '\0') || (*t++ == ' '))) f |= i;
  1985.       }
  1986.       else {            /* no user flags yet */
  1987.     t = strtok (t," ");    /* isolate flag name */
  1988.     sprintf (err,"Unknown flag: %.80s",t);
  1989.     t = strtok (NIL," ");    /* get next flag */
  1990.     mm_log (err,ERROR);
  1991.       }
  1992.     }
  1993.   }
  1994.   return f;
  1995. }
  1996.  
  1997. /* Search support routines
  1998.  * Accepts: MAIL stream
  1999.  *        message number
  2000.  *        pointer to additional data
  2001.  * Returns: T if search matches, else NIL
  2002.  */
  2003.  
  2004.  
  2005. char bezerk_search_all (stream,msgno,d,n)
  2006.     MAILSTREAM *stream;
  2007.     long msgno;
  2008.     char *d;
  2009.     long n;
  2010. {
  2011.   return T;            /* ALL always succeeds */
  2012. }
  2013.  
  2014.  
  2015. char bezerk_search_answered (stream,msgno,d,n)
  2016.     MAILSTREAM *stream;
  2017.     long msgno;
  2018.     char *d;
  2019.     long n;
  2020. {
  2021.   return mail_elt (stream,msgno)->answered ? T : NIL;
  2022. }
  2023.  
  2024.  
  2025. char bezerk_search_deleted (stream,msgno,d,n)
  2026.     MAILSTREAM *stream;
  2027.     long msgno;
  2028.     char *d;
  2029.     long n;
  2030. {
  2031.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  2032. }
  2033.  
  2034.  
  2035. char bezerk_search_flagged (stream,msgno,d,n)
  2036.     MAILSTREAM *stream;
  2037.     long msgno;
  2038.     char *d;
  2039.     long n;
  2040. {
  2041.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  2042. }
  2043.  
  2044.  
  2045. char bezerk_search_keyword (stream,msgno,d,n)
  2046.     MAILSTREAM *stream;
  2047.     long msgno;
  2048.     char *d;
  2049.     long n;
  2050. {
  2051.   return NIL;            /* keywords not supported yet */
  2052. }
  2053.  
  2054.  
  2055. char bezerk_search_new (stream,msgno,d,n)
  2056.     MAILSTREAM *stream;
  2057.     long msgno;
  2058.     char *d;
  2059.     long n;
  2060. {
  2061.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  2062.   return (elt->recent && !elt->seen) ? T : NIL;
  2063. }
  2064.  
  2065. char bezerk_search_old (stream,msgno,d,n)
  2066.     MAILSTREAM *stream;
  2067.     long msgno;
  2068.     char *d;
  2069.     long n;
  2070. {
  2071.   return mail_elt (stream,msgno)->recent ? NIL : T;
  2072. }
  2073.  
  2074.  
  2075. char bezerk_search_recent (stream,msgno,d,n)
  2076.     MAILSTREAM *stream;
  2077.     long msgno;
  2078.     char *d;
  2079.     long n;
  2080. {
  2081.   return mail_elt (stream,msgno)->recent ? T : NIL;
  2082. }
  2083.  
  2084.  
  2085. char bezerk_search_seen (stream,msgno,d,n)
  2086.     MAILSTREAM *stream;
  2087.     long msgno;
  2088.     char *d;
  2089.     long n;
  2090. {
  2091.   return mail_elt (stream,msgno)->seen ? T : NIL;
  2092. }
  2093.  
  2094.  
  2095. char bezerk_search_unanswered (stream,msgno,d,n)
  2096.     MAILSTREAM *stream;
  2097.     long msgno;
  2098.     char *d;
  2099.     long n;
  2100. {
  2101.   return mail_elt (stream,msgno)->answered ? NIL : T;
  2102. }
  2103.  
  2104.  
  2105. char bezerk_search_undeleted (stream,msgno,d,n)
  2106.     MAILSTREAM *stream;
  2107.     long msgno;
  2108.     char *d;
  2109.     long n;
  2110. {
  2111.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  2112. }
  2113.  
  2114.  
  2115. char bezerk_search_unflagged (stream,msgno,d,n)
  2116.     MAILSTREAM *stream;
  2117.     long msgno;
  2118.     char *d;
  2119.     long n;
  2120. {
  2121.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  2122. }
  2123.  
  2124.  
  2125. char bezerk_search_unkeyword (stream,msgno,d,n)
  2126.     MAILSTREAM *stream;
  2127.     long msgno;
  2128.     char *d;
  2129.     long n;
  2130. {
  2131.   return T;            /* keywords not supported yet */
  2132. }
  2133.  
  2134.  
  2135. char bezerk_search_unseen (stream,msgno,d,n)
  2136.     MAILSTREAM *stream;
  2137.     long msgno;
  2138.     char *d;
  2139.     long n;
  2140. {
  2141.   return mail_elt (stream,msgno)->seen ? NIL : T;
  2142. }
  2143.  
  2144. char bezerk_search_before (stream,msgno,d,n)
  2145.     MAILSTREAM *stream;
  2146.     long msgno;
  2147.     char *d;
  2148.     long n;
  2149. {
  2150.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  2151.   return (char) ((long) ((elt->year << 9) + (elt->month << 5) + elt->day) < n);
  2152. }
  2153.  
  2154.  
  2155. char bezerk_search_on (stream,msgno,d,n)
  2156.     MAILSTREAM *stream;
  2157.     long msgno;
  2158.     char *d;
  2159.     long n;
  2160. {
  2161.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  2162.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) == n);
  2163. }
  2164.  
  2165.  
  2166. char bezerk_search_since (stream,msgno,d,n)
  2167.     MAILSTREAM *stream;
  2168.     long msgno;
  2169.     char *d;
  2170.     long n;
  2171. {
  2172.                 /* everybody interprets "since" as .GE. */
  2173.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  2174.   return (char)((long) ((elt->year << 9) + (elt->month << 5) + elt->day) >= n);
  2175. }
  2176.  
  2177.  
  2178. char bezerk_search_body (stream,msgno,d,n)
  2179.     MAILSTREAM *stream;
  2180.     long msgno;
  2181.     char *d;
  2182.     long n;
  2183. {
  2184.   FILECACHE *m = LOCAL->msgs[msgno - 1];
  2185.   return search (m->body,m->bodysize,d,n);
  2186. }
  2187.  
  2188.  
  2189. char bezerk_search_subject (stream,msgno,d,n)
  2190.     MAILSTREAM *stream;
  2191.     long msgno;
  2192.     char *d;
  2193.     long n;
  2194. {
  2195.   char *s = bezerk_fetchstructure (stream,msgno,NIL)->subject;
  2196.   return s ? search (s,strlen (s),d,n) : NIL;
  2197. }
  2198.  
  2199.  
  2200. char bezerk_search_text (stream,msgno,d,n)
  2201.     MAILSTREAM *stream;
  2202.     long msgno;
  2203.     char *d;
  2204.     long n;
  2205. {
  2206.   FILECACHE *m = LOCAL->msgs[msgno - 1];
  2207.   return search (m->header,m->headersize,d,n) ||
  2208.     bezerk_search_body (stream,msgno,d,n);
  2209. }
  2210.  
  2211. char bezerk_search_bcc (stream,msgno,d,n)
  2212.     MAILSTREAM *stream;
  2213.     long msgno;
  2214.     char *d;
  2215.     long n;
  2216. {
  2217.   ADDRESS *a = bezerk_fetchstructure (stream,msgno,NIL)->bcc;
  2218.   LOCAL->buf[0] = '\0';        /* initially empty string */
  2219.                 /* get text for address */
  2220.   rfc822_write_address (LOCAL->buf,a);
  2221.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  2222. }
  2223.  
  2224.  
  2225. char bezerk_search_cc (stream,msgno,d,n)
  2226.     MAILSTREAM *stream;
  2227.     long msgno;
  2228.     char *d;
  2229.     long n;
  2230. {
  2231.   ADDRESS *a = bezerk_fetchstructure (stream,msgno,NIL)->cc;
  2232.   LOCAL->buf[0] = '\0';        /* initially empty string */
  2233.                 /* get text for address */
  2234.   rfc822_write_address (LOCAL->buf,a);
  2235.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  2236. }
  2237.  
  2238.  
  2239. char bezerk_search_from (stream,msgno,d,n)
  2240.     MAILSTREAM *stream;
  2241.     long msgno;
  2242.     char *d;
  2243.     long n;
  2244. {
  2245.   ADDRESS *a = bezerk_fetchstructure (stream,msgno,NIL)->from;
  2246.   LOCAL->buf[0] = '\0';        /* initially empty string */
  2247.                 /* get text for address */
  2248.   rfc822_write_address (LOCAL->buf,a);
  2249.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  2250. }
  2251.  
  2252.  
  2253. char bezerk_search_to (stream,msgno,d,n)
  2254.     MAILSTREAM *stream;
  2255.     long msgno;
  2256.     char *d;
  2257.     long n;
  2258. {
  2259.   ADDRESS *a = bezerk_fetchstructure (stream,msgno,NIL)->to;
  2260.   LOCAL->buf[0] = '\0';        /* initially empty string */
  2261.                 /* get text for address */
  2262.   rfc822_write_address (LOCAL->buf,a);
  2263.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  2264. }
  2265.  
  2266. /* Search parsers */
  2267.  
  2268.  
  2269. /* Parse a date
  2270.  * Accepts: function to return
  2271.  *        pointer to date integer to return
  2272.  * Returns: function to return
  2273.  */
  2274.  
  2275. search_t bezerk_search_date (f,n)
  2276.     search_t f;
  2277.     long *n;
  2278. {
  2279.   long i;
  2280.   char *s;
  2281.   MESSAGECACHE elt;
  2282.                 /* parse the date and return fn if OK */
  2283.   return (bezerk_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  2284.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  2285. }
  2286.  
  2287. /* Parse a flag
  2288.  * Accepts: function to return
  2289.  *        pointer to string to return
  2290.  * Returns: function to return
  2291.  */
  2292.  
  2293. search_t bezerk_search_flag (f,d)
  2294.     search_t f;
  2295.     char **d;
  2296. {
  2297.                 /* get a keyword, return if OK */
  2298.   return (*d = strtok (NIL," ")) ? f : NIL;
  2299. }
  2300.  
  2301.  
  2302. /* Parse a string
  2303.  * Accepts: function to return
  2304.  *        pointer to string to return
  2305.  *        pointer to string length to return
  2306.  * Returns: function to return
  2307.  */
  2308.  
  2309. search_t bezerk_search_string (f,d,n)
  2310.     search_t f;
  2311.     char **d;
  2312.     long *n;
  2313. {
  2314.   char *end = " ";
  2315.   char *c = strtok (NIL,"");    /* remainder of criteria */
  2316.   if (!c) return NIL;        /* missing argument */
  2317.   switch (*c) {            /* see what the argument is */
  2318.   case '{':            /* literal string */
  2319.     *n = strtol (c+1,d,10);    /* get its length */
  2320.     if ((*(*d)++ == '}') && (*(*d)++ == '\015') && (*(*d)++ == '\012') &&
  2321.     (!(*(c = *d + *n)) || (*c == ' '))) {
  2322.       char e = *--c;
  2323.       *c = DELIM;        /* make sure not a space */
  2324.       strtok (c," ");        /* reset the strtok mechanism */
  2325.       *c = e;            /* put character back */
  2326.       break;
  2327.     }
  2328.   case '\0':            /* catch bogons */
  2329.   case ' ':
  2330.     return NIL;
  2331.   case '"':            /* quoted string */
  2332.     if (strchr (c+1,'"')) end = "\"";
  2333.     else return NIL;
  2334.   default:            /* atomic string */
  2335.     if (*d = strtok (c,end)) *n = strlen (*d);
  2336.     else return NIL;
  2337.     break;
  2338.   }
  2339.   return f;
  2340. }
  2341.